#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <cv.h>
#include "config.h"
#include "imagem.h"
#include "gui.h"

static void GUIReiniciarCalibracao();
static void GUIAtribuirMensagem(GtkLabel* label, short int sucesso, gchar* mensagem);
static void GUIComutarSensibilidade(GtkWidget *widget, gpointer user_data);
static GdkPixbuf *IplImage2GdkPixBuf(IplImage *frameBGR, short int flip);
static void FreeGdkBuffer(guchar *pixels, gpointer data);

GUI gui;

int GUIIniciar(int *argc, char **argv[], Imagem *img) {
	guint rc;
	GtkSpinButton *spin_porta;
	gtk_init(argc, argv);
	gui.builder = gtk_builder_new();
	rc = gtk_builder_add_from_file(gui.builder, UIFILE, NULL);
	if(!rc)
		return 0;
	gui.janela = (GtkWindow *) gtk_builder_get_object(gui.builder, "window1");
	gtk_builder_connect_signals(gui.builder, NULL);
	GUIReiniciarCalibracao();
	spin_porta = (GtkSpinButton *) gtk_builder_get_object(gui.builder, "spin_porta");
	gtk_spin_button_set_value(spin_porta, 1987);
	//Inicializando valores da estrutura
	gui.porta = 1987;
	gui.execucao = 0;
	gui.idtimeout = 0;
	gui.img = img;
	gui.pid = 0;
	return 1;
}

void GUIExibir() {
	gtk_widget_show_all((GtkWidget *) gui.janela);
	gtk_main();
	return;
}

G_MODULE_EXPORT short int GUIEscolherDispositivo(GtkWidget *widget, gpointer data) {
	GtkSpinButton *spin_dispositivo;
	GtkLabel *label_camera;
	GtkContainer *frm_calibracao;
	GtkContainer *frm_openoffice;
	frm_calibracao = (GtkContainer *) gtk_builder_get_object(gui.builder, "frame_calibracao");
	frm_openoffice = (GtkContainer *) gtk_builder_get_object(gui.builder, "frame_openoffice");
	gint dispositivo;
	short int estado;
	gchar msg[100];

	//GUIReiniciarCalibracao();
	label_camera = (GtkLabel *) gtk_builder_get_object(gui.builder, "label_camera");
	spin_dispositivo = (GtkSpinButton *) gtk_builder_get_object(gui.builder, "spin_dispositivo");
	dispositivo = gtk_spin_button_get_value_as_int(spin_dispositivo);
	if(ImagemVerificarConexao(gui.img))
		ImagemLiberar(gui.img);
	//Tentando inicializar a imagem
	if(ImagemConectar(gui.img, dispositivo)) {
	    ImagemAlterarBrilho(gui.img, NORMAL_BRILHO);
	    ImagemAlterarContraste(gui.img, NORMAL_CONTRASTE);	
		g_sprintf(msg, "Dispositivo %d inicializado com sucesso.", dispositivo);
		GUIAtribuirMensagem(label_camera, 1, msg);
		gui.idtimeout = g_timeout_add(200, GUIObterFrame, NULL);
		GUIObterFrame();
		estado = 1;
	}
	else {
		g_sprintf(msg, "Sem comunicação com o dispositivo %d.", dispositivo);
		GUIAtribuirMensagem(label_camera, 0, msg);
		estado = 0;
	}
	gtk_container_foreach(frm_calibracao, GUIComutarSensibilidade, &estado);
	gtk_container_foreach(frm_openoffice, GUIComutarSensibilidade, &estado);
	return estado;
}

gboolean GUIObterFrame() {
	IplImage *imagem_antiga;
	GdkPixbuf *pixbuf;
	GtkImage *widget_imagem;
	if(!gui.execucao && ImagemVerificarConexao(gui.img)) {
		widget_imagem = (GtkImage *) gtk_builder_get_object(gui.builder, "imagem");
		ImagemObterFrame(gui.img);
        ImagemDetectarLaser(gui.img);
        ImagemDestacarLaser(gui.img);
		imagem_antiga = ImagemObterDados(gui.img);
		pixbuf = IplImage2GdkPixBuf(imagem_antiga, 0);
		gtk_image_set_from_pixbuf(widget_imagem, pixbuf);
		g_object_unref(G_OBJECT(pixbuf));
		return 1;
	}
	else
		return 0;
}

G_MODULE_EXPORT void GUICalibrar(GtkWidget *widget, gpointer data) {
	GtkLabel *label_calibracao;
	GUIReiniciarCalibracao();
	gui.etapa_calibracao = 1;
	label_calibracao = (GtkLabel *) gtk_builder_get_object(gui.builder, "label_calibracao");
	gtk_label_set_markup(label_calibracao, "Clique no limite superior esquerdo...");
}

G_MODULE_EXPORT gboolean GUIObterCoordenadas(GtkWidget *eventbox, GdkEventButton *event, gpointer data) {
	GtkLabel *label_calibracao;
	label_calibracao = (GtkLabel *) gtk_builder_get_object(gui.builder, "label_calibracao");
	switch(gui.etapa_calibracao) {
		case 1:
			gui.calib_topleft.x = 2 * event->x;
			gui.calib_topleft.y = 2 * event->y;
			gtk_label_set_markup(label_calibracao, "Clique no limite inferior direito...");
			gui.etapa_calibracao++;
			break;
		case 2:
			gui.calib_bottomright.x = 2 * event->x;
			gui.calib_bottomright.y = 2 * event->y;
			GUIAtribuirMensagem(label_calibracao, 1, "Calibração feita com sucesso");
			gui.etapa_calibracao++;
			break;
		default:
			break;
	}
	return 1;
}

G_MODULE_EXPORT void GUIAbrirOO(GtkWidget *widget, gpointer data) {
	//@TODO: aprimorar launcher e disponibilizá-lo nas versões windows.
	char cmd[150];
	char params[100];
	GtkSpinButton *spin_porta;
	FILE *fd;
	spin_porta = (GtkSpinButton *) gtk_builder_get_object(gui.builder, "spin_porta");
	gui.porta = gtk_spin_button_get_value_as_int(spin_porta);
    g_sprintf(params, "-accept=\"socket,host=localhost,port=%ld;urp;\"", gui.porta);  
#if defined(linux)
    g_sprintf(cmd, "ooimpress %s", params);
    fd = popen(cmd, "r"); //apenas para abrir o processo sem espera ocupada...
    fclose(fd);           //arquivo de pipeline não será usado
#elif defined(WIN32) || defined(MINGW32)
    g_sprintf(cmd, "start simpress.exe %s", params);
    system(cmd);
#endif 
}

G_MODULE_EXPORT void GUIConectarOO(GtkWidget *widget, gpointer data) {
	int rc;
	int estado;
	char cmd[6][40];
	char msg[100];
	pid_t pid;
	GtkToggleButton *botao_conectar;
	GtkImage *widget_imagem;
	GtkSpinButton *spin_porta;
	GtkLabel *label_openoffice;
	GtkContainer *frm_calibracao, *frm_camera;
	label_openoffice = (GtkLabel *) gtk_builder_get_object(gui.builder,
														   "label_openoffice");
	frm_calibracao = (GtkContainer *) gtk_builder_get_object(gui.builder,
										 				   "frame_calibracao");
	frm_camera = (GtkContainer *) gtk_builder_get_object(gui.builder,
															 "frame_camera");
	if(gui.execucao == 1) {
		//Ação de desconectar
#if defined(WIN32) || defined(MINGW32)
        //@TODO: implementar equivalente do KILL para windows aqui
        ;
#elif defined(linux)
		kill(gui.pid, SIGTERM);
#endif
		gui.execucao = 0;
GtkContainer *frm_openoffice;
	frm_calibracao = (GtkContainer *) gtk_builder_get_object(gui.builder, "frame_calibracao");
		g_sprintf(msg, "Tentando comunicar na porta %d", gui.porta);
		GUIAtribuirMensagem(label_openoffice, 0, "Sem comunicação com o OpenOffice.Org");
		estado = 1;
		gtk_container_foreach(frm_camera, GUIComutarSensibilidade, &estado);
		GUIEscolherDispositivo(NULL, NULL);
	}
	else {
		//Ação de conectar
		//Atualizando o valor da porta
		spin_porta = (GtkSpinButton *) gtk_builder_get_object(gui.builder,
															  "spin_porta");
		gui.porta = gtk_spin_button_get_value_as_int(spin_porta);

		//Desabilitando sensibilidade do frame de calibração
		estado = 0;
		gtk_container_foreach(frm_calibracao, GUIComutarSensibilidade, &estado);
		gtk_container_foreach(frm_camera, GUIComutarSensibilidade, &estado);

		//Desabilitar aquisição de imagens pelo processo onde o GTK é executado.
		gui.execucao = 1;
		gtk_timeout_remove(gui.idtimeout);
		ImagemLiberar(gui.img);


		//Atualizar a label
		//@TODO: verificar estado do penserver para saber se a conexão foi bem sucedida
		g_sprintf(msg, "Tentando comunicar na porta %d", gui.porta);
		GUIAtribuirMensagem(label_openoffice, 1, msg);

		//Iniciando pencapture
		g_printf(
		    "Iniciando pencapture...\n"
		    "Porta: %d\n"
		    "Dispositivo: %d\n"		    
		    "Coordenadas do limite superior esquerdo: %d, %d\n"
		    "Coordenadas do limite inferior direito: %d, %d\n",
		    gui.porta, (gui.img)->deviceno,
		    gui.calib_topleft.x, gui.calib_topleft.y,
		    gui.calib_bottomright.x, gui.calib_bottomright.y	    
		);
		g_sprintf(cmd[0], "--porta=%d", gui.porta);
		g_sprintf(cmd[1], "--dispositivo=%d ", (gui.img)->deviceno);
		g_sprintf(cmd[2], "--topleftx=%d", gui.calib_topleft.x);
		g_sprintf(cmd[3], "--toplefty=%d", gui.calib_topleft.y);
		g_sprintf(cmd[4], "--bottomrightx=%d", gui.calib_bottomright.x);
		g_sprintf(cmd[5], "--bottomrighty=%d", gui.calib_bottomright.y);
#if defined(linux)
		pid = vfork();
		if(pid == -1) {
			//@TODO: abrir msg box
			perror("Falha ao abrir novo processo...");
			exit(EXIT_FAILURE);
		}
		else if (pid == 0) {
			execlp("pencapture", "pencapture",
					cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], (char *) NULL);
			_exit(EXIT_FAILURE);
		}
		else {
			gui.pid = pid;
		}
#elif defined(WIN32) || defined(MINGW32)
    char wincmd[200];
    g_sprintf(wincmd, "start pencapture.exe %s %s %s %s %s",
              cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
    system(wincmd);
    ;
#endif
	}
}

G_MODULE_EXPORT void GUIExibirAjuda(GtkWidget *widget, gpointer data) {
#if defined(__MINGW32__) || defined(WIN32)
	system("start " MANUALFILE);
#else
	system("gnome-open " MANUALFILE);
#endif
}

G_MODULE_EXPORT void GUIExibirSobre(GtkWidget *widget, gpointer data) {
	GtkWidget *dialog = GTK_WIDGET (gtk_builder_get_object (gui.builder, "dialogo_sobre"));
	gtk_dialog_run(GTK_DIALOG (dialog));
	gtk_widget_hide(dialog);
}

G_MODULE_EXPORT void GUISair(GtkWidget *widget, gpointer data) {
	if(gui.execucao) {
#if defined(linux)
		kill(gui.pid, SIGTERM);
#endif
		gui.execucao = 0;
	}
	gtk_main_quit();

}

static void GUIReiniciarCalibracao() {
	GtkLabel *label_calibracao;
	gui.etapa_calibracao = 0;
	gui.calib_topleft.x = gui.calib_topleft.y = -1;
	gui.calib_bottomright.x = gui.calib_bottomright.y = -1;
	label_calibracao = (GtkLabel *) gtk_builder_get_object(gui.builder, "label_calibracao");
	gtk_label_set_markup(label_calibracao,
						 "<span foreground='#A00000' font_weight='bold'>"
						 "Calibração ainda não foi feita.</span>");
}

static void GUIAtribuirMensagem(GtkLabel* label, short int sucesso, gchar* mensagem) {
	gchar msgfinal[200];
	if(sucesso)
		g_sprintf(msgfinal, "<span foreground='#0000A0' font_weight='bold'>%s</span>", mensagem);
	else
		g_sprintf(msgfinal, "<span foreground='#A00000' font_weight='bold'>%s</span>", mensagem);
	gtk_label_set_markup(label, msgfinal);
}

static void GUIComutarSensibilidade(GtkWidget *widget, gpointer user_data) {
	short int *estado = (short int *) user_data;
	gtk_widget_set_sensitive(widget, *estado);
}

static GdkPixbuf *IplImage2GdkPixBuf(IplImage  *frameBGR, short int flip) {
	GdkPixbuf *framePixbuf;
	GdkPixbuf *scaledPixbuf;
	IplImage *frameNovo = cvCloneImage(frameBGR);
	gboolean hasAlpha = 0;
	int flagFlip = flip ? CV_CVTIMG_FLIP : 0;
	cvConvertImage(frameBGR, frameNovo, CV_CVTIMG_SWAP_RB | flagFlip );
	if (gui.calib_topleft.x != -1 && gui.calib_bottomright.x != -1)
		cvRectangle(frameNovo, gui.calib_topleft, gui.calib_bottomright, CV_RGB(0,0,80), 2, 8, 0);
	framePixbuf = gdk_pixbuf_new_from_data (
		(guchar *)frameNovo->imageData,
		GDK_COLORSPACE_RGB,
		hasAlpha,
		frameNovo->depth,
		frameNovo->width,
		frameNovo->height,
		frameNovo->widthStep,
		FreeGdkBuffer, //função de liberação de memória
		&frameNovo //dados para a função de liberação de memória
	);
	scaledPixbuf = gdk_pixbuf_scale_simple(framePixbuf, 320, 240, GDK_INTERP_BILINEAR);
	g_object_unref(G_OBJECT(framePixbuf));
	return scaledPixbuf;
}

static void FreeGdkBuffer(guchar *pixels, gpointer data) {
	IplImage** dados = (IplImage **) data;
	cvReleaseImage(dados);
}
